Skip to content

Make library search and category filters deep-linkable#50

Open
HMAKT99 wants to merge 2 commits into
Forward-Future:mainfrom
HMAKT99:feat/url-filter-state
Open

Make library search and category filters deep-linkable#50
HMAKT99 wants to merge 2 commits into
Forward-Future:mainfrom
HMAKT99:feat/url-filter-state

Conversation

@HMAKT99

@HMAKT99 HMAKT99 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

The homepage library's filter state (search query, active category, page) lived only in memory. As a result:

  • a filtered or searched view couldn't be shared or bookmarked,
  • a reload dropped the filters back to the default view, and
  • the browser back/forward buttons didn't restore previous filter states.

This wires that state into the URL query string so a slice of the library is directly linkable — a natural fit for a catalog that's explicitly built for SEO/GEO and agent discovery (llms.txt, catalog.json, etc.).

Behavior

  • Search → ?q=…, category → ?category=engineering, page → ?page=2. Defaults (empty query, all, page 1) are omitted so the canonical homepage URL stays clean.
  • On load and on popstate, state is restored from the URL. The category is validated against the available filter chips (unknown values fall back to all) and the page is parsed/clamped by the existing pagination logic, so hand-edited or stale URLs degrade gracefully.
  • Uses history.replaceState (guarded for environments without it), so typing in the search box doesn't flood the history stack.

Implementation notes

  • Extracted the category-activation logic into a reusable applyCategory() (used by both the click handler and URL restore).
  • syncUrlState() is called at the end of updateLibrary(), which already runs on every search/category/pagination change, so there's a single write path. It only calls replaceState when the URL actually changes.
  • No-ops cleanly on pages without the library (the searchInput/results guards already short-circuit updateLibrary).

Verification

  • node scripts/check.mjsLoop Library checks passed.
  • node --check site/script.js
  • Builders (build-skill-catalog, build-loop-pages, build-social-images) → no artifact drift.
  • Manual: searching, switching categories, and paging update the URL; reloading and back/forward restore the same view; ?category=bogus / ?page=99 normalize to a valid state.

The homepage filter state lived only in memory, so a filtered or searched
view could not be shared or bookmarked, a reload dropped it, and the
browser back button did not restore it.

Reflect the search query, active category, and page in the URL query
string (q, category, page), omitting defaults to keep clean URLs tidy.
On load and on popstate, restore that state from the URL, validating the
category against the available filters and clamping the page. This makes
filtered views shareable and bookmarkable and gives back/forward the
behavior visitors expect, which also suits an SEO- and agent-oriented
catalog where linking directly to a slice of the library is useful.
@HMAKT99

HMAKT99 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

This one's a feature rather than a bug fix, so completely understand if it's not a direction you want — feel free to close it, no hard feelings.

The idea: the homepage filter state (search, category, page) lives only in memory, so a filtered view can't be shared or bookmarked, a reload drops it, and back/forward don't restore it. This reflects it in the URL (?q=, ?category=, ?page=), omitting defaults to keep canonical URLs clean, and restores + validates it on load and popstate. It felt like a natural fit for a catalog that's explicitly built for linking and agent discovery, but I get that it adds surface area.

If you're open to it but want it scoped differently (e.g. query-only, no page param), I'm glad to trim. Check suite + builders pass with no artifact drift.

@mberman84 mberman84 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL state is bookmarkable, but three blocking issues remain:

  1. syncUrlState() starts with a fresh URLSearchParams, so loading ?utm_source=newsletter&q=agent immediately deletes utm_source and any other URL state the library does not own. Initialize from window.location.search, then set/delete only q, category, and page.
  2. Every interaction uses replaceState. Browser verification confirmed that after changing category or page, Back leaves the library instead of restoring the prior filter state, so the new popstate handler cannot deliver the behavior claimed by the PR. Use pushState for meaningful category/pagination navigation boundaries; reserve replaceState for initial canonicalization or transient search typing.
  3. This changes site/script.js without bumping the shared versioned script URL. Update the emitted/validated asset version and regenerate affected pages.

… version

Address review on the deep-linkable filters:
1. syncUrlState() now initializes from window.location.search and only
   sets/deletes q, category, and page, so unrelated parameters such as
   utm_source survive instead of being dropped.
2. Category and pagination changes now use pushState so the back button
   restores the prior filter state; replaceState is reserved for initial
   canonicalization and transient search typing.
3. Bump the shared script.js asset version to 20260621-url-state across the
   homepage, Learn/Agents pages, the loop-page generator, and
   scripts/check.mjs, then regenerate the loop pages.
@HMAKT99

HMAKT99 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

All three addressed:

  1. syncUrlState() now seeds from window.location.search and only sets/deletes q/category/page, so utm_source and any other foreign params survive.
  2. Category and pagination changes use pushState (Back now restores the prior filter state); replaceState is reserved for initial canonicalization and transient search typing. popstate stays read-only.
  3. Bumped script.js to 20260621-url-state everywhere + regenerated the loop pages.
    Check suite + builders green.

@HMAKT99

HMAKT99 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@mberman84 ready for re-review whenever you have time — foreign params preserved, pushState on navigation boundaries, version bumped. Appreciate the detailed feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants